﻿#region Apache License Version 2.0
/*----------------------------------------------------------------

Copyright 2024 DJ Zheng & ShangHai GM Nahefa Technology Co.,Ltd.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.

----------------------------------------------------------------*/
#endregion Apache License Version 2.0

using System.IO;
using System.Linq;
using System.Text;
using System.Threading;  // use to sleep
using Collector.Device;

namespace Collector
{
    public class PulseThreeBodyContinuous : PulseGeneralCollector
    {
        private readonly string TAG = "PulseThreeBodyContinuous:";
        private readonly byte CMD_OFFSET = 0x05;
        private IDeviceCommunication mDeviceCommunication;
        private ReaderWriterLockSlim mRWLock = new ReaderWriterLockSlim();
        private AutoResetEvent conditionWait = new AutoResetEvent(false);
        private bool isRapidPhaseFinished = false;
        // 指示连续采脉状态转换
        private ContinuousState mState;
        // 指示设备反馈的真实状态码
        private ContinuousResponseCode mCode;
        // 脉象原始数据保留文件
        private string mRawDataFile;

        public delegate int PulseContinuousNotifyDelegate<T>(T obj, PulseCommonHelper.Display.DisplayType type);
        public PulseContinuousNotifyDelegate<PulseCommonHelper.Display> notifyMainForm;

        public enum ContinuousState
        {
            // 设备空闲
            eCONTINUOUS_STATE_IDLE,
            // 设备开始连续采集，处于快速加压中
            eCONTINUOUS_STATE_STARTED,
            eCONTINUOUS_STATE_PREPARE_MEASURING, // no use
            // 设备处于匀速加压采集数据中
            eCONTINUOUS_STATE_MEASURING,
            // 设备连续采集完成，设备正在复位中
            eCONTINUOUS_STATE_FINISHED,
        }

        public enum ContinuousResponseCode
        {
            eCODE_RAPID_FINISHED = 0x00,
            eCODE_CONTINUOUS_NORMAL_FINISHED,
            eCODE_CONTINUOUS_FINISHED_WITH_TIMEOUT,
            eCODE_RAPID_WITH_TIMEOUT,
            eCODE_RAPID_ACK_WITH_TIMEOUT = 0x04,
            eCODE_DEVICE_OVERRIDE = 0xA6
        }

        public PulseThreeBodyContinuous()
        {

        }

        public IDeviceCommunication DeviceCommunication
        {
            get { return mDeviceCommunication; }
            set { DeviceAdapter = mDeviceCommunication = value; }
        }

        public string RawDataFile
        {
            get { return mRawDataFile; }
            set { mRawDataFile = value; }
        }

        public bool ResetDevice()
        {
            return mDeviceCommunication.ResetDevice();
        }

        public void ResetContinuousConfigure()
        {
            isRapidPhaseFinished = false;
            mRawDataFile = null;
            mState = ContinuousState.eCONTINUOUS_STATE_IDLE;
            mCode = ContinuousResponseCode.eCODE_RAPID_FINISHED;
        }

        public bool ProcessContinuous()
        {
            // Send command to Device.
            byte[] payload;
            bool ret = mDeviceCommunication.SendCommandToDevice("SPHeadmmHg_ConsecutiveData", 0xD0, out payload);
            if (ret && payload[0] == 0x00)
            {
                string message = string.Format("->连续加压, Waiting for...");
                DisplayMessageToUI(message, LogMsgType.DEBUG);
                SetCollectorState(ContinuousState.eCONTINUOUS_STATE_STARTED);
                // 等待加压到位，等待100s
                ret = conditionWait.WaitOne(100 * 1000);
            }
            else
            {
                ret = false;
            }

            return ret;
        }

        public override void ControlFrameProcessing(byte[] frame)
        {
            DeviceEvent evt = (DeviceEvent)frame[CMD_OFFSET];
            Log.Debug(TAG + "ControlFrameProcessing() {0} evt code: {1}", ConvertFuncs.ByteArrayToHexString(frame), evt);
            switch (evt)
            {
                case DeviceEvent.eDEV_EVENT_CONTINUOUS:
                {
                    // 回复设备收到D1事件
                    ProcessResponseData(frame, frame.Length);

                }
                break;                
                case DeviceEvent.eDEV_EVENT_OVERFLOW:
                {   
                    // Check pump exceeding the maximum pressure value.              
                    if (mState != ContinuousState.eCONTINUOUS_STATE_FINISHED)
                    {
                        SetCollectorState(ContinuousState.eCONTINUOUS_STATE_FINISHED);
                        string message = string.Format("->连续加压, 匀速加压过载,设备复位中...");
                        DisplayMessageToUI(message, LogMsgType.DEBUG);
                    }
                }
                break;
                case DeviceEvent.eDEV_EVENT_RESET:
                {
                    // 回复设备收到A4事件
                    if (mDeviceCommunication != null)
                    {
                        mDeviceCommunication.GivebackCompleted(evt);
                    }

                    Log.Debug("ControlFrameProcessing device already reset completed");
                    // 更新状态
                    SetCollectorState(ContinuousState.eCONTINUOUS_STATE_IDLE, true);
                    
                }
                break;
                default:
                {
                    base.ControlFrameProcessing(frame);
                    break;
                }
            }
        }

        public override void PulseFrameProcessing(byte[] frame)
        {
            if (mState != ContinuousState.eCONTINUOUS_STATE_MEASURING)
            {
                return;
            }

            if (mRawDataFile != null)
                ByteToFile(frame, mRawDataFile);
        }

        public override void AirFrameProcessing(byte[] frame)
        {

        }

        public void SetCollectorState(ContinuousState state, bool needRelease = false)
        {
            mRWLock.TryEnterWriteLock(200);
            try
            {
                if (mState != state)
                {
                    mState = state;
                    // 通知状态更新
                    if (needRelease)
                        conditionWait.Set();
                }
            }
            finally
            {
                mRWLock.ExitWriteLock();
            }
        }

        public ContinuousState GetContinuousState()
        {
            ContinuousState state = ContinuousState.eCONTINUOUS_STATE_IDLE;
            mRWLock.TryEnterReadLock(200);
            state = mState;
            mRWLock.ExitReadLock();

            return state;
        }      

        public void ProcessResponseData(byte[] frame, int size)
        {
            //byte code = (size > 6) ? frame[6] : (byte)0x0;
            if (frame.Length < 7)
                return;

            string dataString = ToHexStringStringBuilder(frame);
            Log.Debug(TAG + "ProcessResponseData() size:" + size +
                            ", data: " + dataString);

            DeviceContinuousResponseCode code = (DeviceContinuousResponseCode)frame[IDeviceProtocolShape.FRAME_FIELD_PAYLOAD_OFFSET];
            switch (code)
            {
                case DeviceContinuousResponseCode.eREPORT_STATE_RAPID_PHASE_COMPLETED:
                {
                    // 该code主机必须应答设备
                    mDeviceCommunication.SendCommandToDevice("SPResponseRapid_ConsecutiveData");

                    string message = string.Format("->连续加压, 快速加压正常完成，主机已响应");
                    DisplayMessageToUI(message, LogMsgType.DEBUG);

                    SetCollectorState(ContinuousState.eCONTINUOUS_STATE_MEASURING);

                    message = string.Format("->连续加压, 正在匀速加压,请等待...");
                    DisplayMessageToUI(message, LogMsgType.DEBUG);
                }
                break;
                case DeviceContinuousResponseCode.eREPORT_STATE_MEASURE_PHASE_COMPLETED:
                {
                    string message = string.Format("->连续加压, 匀速加压正常完成");
                    DisplayMessageToUI(message, LogMsgType.DEBUG);

                    SetCollectorState(ContinuousState.eCONTINUOUS_STATE_FINISHED);
                }
                break;
                case DeviceContinuousResponseCode.eREPORT_STATE_MEASURE_COMPLETED_WITH_TIMEOUT:
                {

                    string message = string.Format("->连续加压, 匀速加压超时完成");
                    DisplayMessageToUI(message, LogMsgType.DEBUG);
                    // device auto reset
                    SetCollectorState(ContinuousState.eCONTINUOUS_STATE_FINISHED);
                }
                break;
                case DeviceContinuousResponseCode.eREPORT_STATE_RAPID_PHASE_TIMEOUT:
                {
                    string message = string.Format("->连续加压, 快速加压超时");
                    DisplayMessageToUI(message, LogMsgType.ERROR);
                    // device auto reset
                    SetCollectorState(ContinuousState.eCONTINUOUS_STATE_FINISHED);

                }
                break;
                case DeviceContinuousResponseCode.eREPORT_STATE_RAPID_PHASE_COMPLETED_WITHOUT_ACK:
                {
                    string message = string.Format("->连续加压, 快速加压完成，等待主机响应超时");
                    DisplayMessageToUI(message, LogMsgType.ERROR);
                    // Maybe to reset device
                    SetCollectorState(ContinuousState.eCONTINUOUS_STATE_FINISHED);
                }
                break;
            }
        }

        private static bool ByteToFile(byte[] byteArray, string fileName)
        {
            bool result = false;
            byte[] padding = { 0x00, 0x00, 0x00, 0x00, 0x00 };
            try
            {
                using (FileStream fs = new FileStream(fileName, FileMode.Append, FileAccess.Write))
                {
                    fs.Write(byteArray, 0, byteArray.Length);
                    //fs.Write(padding, 0, 5);
                    result = true;
                }
            }
            catch
            {
                result = false;
            }
            return result;
        }

        private void DisplayMessageToUI(string message, LogMsgType type)
        {
            PulseCommonHelper.Display display = new PulseCommonHelper.Display();
            display.displayMsgBox.message = message;
            display.displayMsgBox.type = type;
            if (notifyMainForm != null)
            {
                notifyMainForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_MSG);
            }
        }

        private void DisplayMessageToUI(string itemName, string itemInfo, bool result)
        {
            PulseCommonHelper.Display display = new PulseCommonHelper.Display();
            display.displayRltBox.itemName = itemName;
            display.displayRltBox.itemInfo = itemInfo;
            display.displayRltBox.result = result;
            if (notifyMainForm != null)
            {
                notifyMainForm(display, PulseCommonHelper.Display.DisplayType.eDISPLAY_TYPE_ITEM);
            }
        }

        private string ToHexStringStringBuilder(byte[] bytes)
        {
            StringBuilder hex = new StringBuilder(bytes.Length * 2);
            foreach (byte b in bytes)
            {
                hex.AppendFormat("{0:x2} ", b);
            }
            return hex.ToString();
        }
    }
}
